package com.beyondsoft.common.core.sys.aop;

import com.beyondsoft.common.core.constant.TransactionConstants;
import com.beyondsoft.common.core.exchange.sender.SubToMainSender;
import com.beyondsoft.common.core.redis.RedisMsService;
import com.beyondsoft.common.core.sys.exception.GeneralException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;

@Aspect
@Component
public class MsAspect {

    @Autowired
    private RedisMsService redisMsService;
    @Autowired
    private PlatformTransactionManager transactionManager;
    @Autowired
    private SubToMainSender subToMainSender;
    //这里默认feign超时时间
    @Value("${feign.client.config.default.connectTimeout}")
    private Long feignConnectTimeout;

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.beyondsoft.common.core.sys.annotations.MsTransactional)")
    public void msPointCut() {

    }

    /**
     * 把before和after结合起来处理
     *
     * @param proceedingJoinPoint
     * @throws Throwable
     */
    @Around("msPointCut()")
    public Object msAround(ProceedingJoinPoint proceedingJoinPoint) {
        // start stopwatch   相当于是before advice
        //开启事务
        DefaultTransactionDefinition def = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        //操作线程名
        String thread = null;
        //redis key
        String packageKey = null;
        // main key
        String mainPackageKey = null;
        //获取请求
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        thread = request.getHeader("thread");
        //主线程 非remote
        if (thread == null) {
            //随机一个线程名
            String randomThread = DigestUtils.md5DigestAsHex(UUID.randomUUID().toString().getBytes()) + ":" + Thread.currentThread().getId();
            //设置线程名
            Thread.currentThread().setName(randomThread);
            thread = randomThread;
            //redis key
            packageKey = TransactionConstants.PACKAGEKEY + thread;
            //初始化线程事务状态
            redisMsService.set(packageKey, TransactionConstants.PACKAGEKEY_STATUS_INIT);
        } else {
            //redis key remote已经在SubscribeAop注册了，在controller request的时候注册
            packageKey = TransactionConstants.PACKAGEKEY + thread + ":" + request.getMethod() + "-" + request.getRequestURI();
        }
        //main key
        mainPackageKey = TransactionConstants.PACKAGEKEY + thread;
        //-------------------------------------------------
        Object retVal = null;
        try {
            retVal = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            // 发生异常
            redisMsService.set(packageKey, TransactionConstants.PACKAGEKEY_STATUS_ERROE);
            throw new GeneralException(throwable);
        } finally {
            // stop stopwatch    相当于是after advice
            //执行完毕
            Integer handle = (Integer) redisMsService.get(packageKey);
            //在执行过程中没有设置过才算执行成功
            if (handle == null || handle == TransactionConstants.PACKAGEKEY_STATUS_INIT) {
                //更新状态
                redisMsService.set(packageKey, TransactionConstants.PACKAGEKEY_STATUS_SUCCESS);
            }
            //通知主服务已经有了执行结果
            subToMainSender.send(mainPackageKey);
            //线程上锁
            Long lock = TransactionConstants.getTransactionLock(mainPackageKey);
            synchronized (lock) {
                try {
                    lock.wait(feignConnectTimeout);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //获取服务处理状态
            Integer state = TransactionConstants.getTransactionState(mainPackageKey);
            System.out.println("处理结果" + state);
            //超时处理
            if (state == TransactionConstants.PACKAGEKEY_STATUS_SUCCESS) {
                transactionManager.commit(status);
            } else {
                transactionManager.rollback(status);
                //捕获超时的情况
                // 发生异常
                redisMsService.set(packageKey, TransactionConstants.PACKAGEKEY_STATUS_ERROE);
                //通知事务组执行失败
                subToMainSender.send(mainPackageKey);
            }
            //执行完毕清除锁
            TransactionConstants.clearTransactionLock(mainPackageKey);
            //执行完毕清除事务
            TransactionConstants.clearTransactionState(mainPackageKey);
            System.out.println("执行完了");
        }
        return retVal;
    }

}
